import Blockly from 'blockly/core'
import 'blockly/python_compressed'
import 'blockly/blockly_compressed'
import 'blockly/blocks_compressed'

Blockly.Blocks.function_def = {
  init: function () {
    this.appendDummyInput()
      .appendField('define')
      .appendField(new Blockly.FieldTextInput('def_name'), 'FUNCNAME')
    this.appendStatementInput('DEFSTATE').appendField('statements')
    this.setColour(230)
    this.updateShape_()
    this.setMutator(new Blockly.Mutator(['def_create_with_item']))
    this.setPreviousStatement(true, null)
    this.setNextStatement(true, null)
    this.itemCount_ = 3
    this.setHelpUrl()
    this.setTooltip()
  },
  compose: function (containerBlock) {
    let itemBlocks = containerBlock.getInputTargetBlock('STACK')
    const NewConnections = []
    while (itemBlocks && !itemBlocks.isInsertionMarker()) {
      NewConnections.push(itemBlocks.valueConnection_)
      itemBlocks =
        itemBlocks.nextConnection && itemBlocks.nextConnection.targetBlock()
    }
    for (let i = 0; i < this.itemCount_; i++) {
      const connection = this.getInput('ADD' + i).connection.targetConnection
      if (connection && NewConnections.indexOf(connection) === -1) {
        connection.disconnect()
      }
    }
    // update instant state
    this.itemCount_ = NewConnections.length
    this.updateShape_()
    for (let i = 0; i < this.itemCount_; i++) {
      Blockly.Mutator.reconnect(NewConnections[i], this, 'ADD' + i)
    }
  },

  domToMutation: function (xmlElement) {
    this.itemCount_ = parseInt(xmlElement.getAttribute('items'), 10)
    this.updateShape_()
  },

  decompose: function (workspace) {
    const containerBlocks = workspace.newBlock('def_create_with_container')
    containerBlocks.initSvg()
    let connectionBlock = containerBlocks.getInput('STACK').connection
    for (let j = 0; j < this.itemCount_; j++) {
      const itemBlocks = workspace.newBlock('def_create_with_item')
      itemBlocks.initSvg()
      connectionBlock.connect(itemBlocks.previousConnection)
      connectionBlock = itemBlocks.nextConnection
    }
    return containerBlocks
  },
  saveConnections: function (containerBlock) {
    let itemBlocks = containerBlock.getInputTargetBlock('STACK')
    let i = 0
    while (itemBlocks) {
      const inputValue = this.getInput('ADD' + i)
      itemBlocks.valueConnection_ =
        inputValue && inputValue.connection.targetConnection
      i++
      itemBlocks =
        itemBlocks.nextConnection && itemBlocks.nextConnection.targetBlock()
    }
  },

  mutationToDom: function () {
    const containerElement = Blockly.utils.xml.createElement('mutation')
    containerElement.setAttribute('items', this.itemCount_)
    return containerElement
  },

  updateShape_: function () {
    if (this.itemCount_ && this.getInput('EMPTY')) {
      this.removeInput('EMPTY')
    } else if (!this.itemCount_ && !this.getInput('EMPTY')) {
      this.appendDummyInput('EMPTY').appendField('argument')
    }
    let replace = 0
    for (let i = 0; i < this.itemCount_; i++) {
      if (!this.getInput('ADD' + i)) {
        const input = this.appendValueInput('ADD' + i).setAlign(
          Blockly.ALIGN_RIGHT
        )
        if (i === 0) {
          input.appendField('argument')
        }
      }
      replace = i
    }
    replace++
    // Remove deleted inputs.
    while (this.getInput('ADD' + replace)) {
      this.removeInput('ADD' + replace)
      replace++
    }
  }
}

Blockly.Blocks.def_create_with_container = {
  init: function () {
    this.setStyle('list_blocks')
    this.appendStatementInput('STACK')
    this.appendDummyInput().appendField('add argument')
    this.contextMenu = false
    this.setTooltip('add argument')
  }
}

Blockly.Blocks.def_create_with_item = {
  /**
   * Mutator block for adding items.
   * @this {Blockly.Block}
   */
  init: function () {
    this.setStyle('list_blocks')
    this.appendDummyInput().appendField('argument')
    this.setPreviousStatement(true)
    this.setNextStatement(true)
    this.setTooltip('def')
    this.contextMenu = false
  }
}

Blockly.Python.function_def = function (block) {
  // Create a list with any number of elements of any type.
  const defName = block.getFieldValue('FUNCNAME')
  const statement = Blockly.Python.statementToCode(block, 'DEFSTATE')
  const elements = new Array(block.itemCount_)
  for (let i = 0; i < block.itemCount_; i++) {
    elements[i] =
      Blockly.Python.valueToCode(block, 'ADD' + i, Blockly.Python.ORDER_NONE) ||
      'None'
  }
  const code =
    'def ' + defName + '(' + elements.join(', ') + '):\n' + statement
  return code
}

Blockly.Blocks.callback_def_with_return = {
  init: function () {
    this.appendDummyInput().appendField(
      new Blockly.FieldLabelSerializable(''),
      'FUNCNAME'
    )
    this.setHelpUrl()
    this.setColour(230)
    this.itemCount_ = 3
    this.updateShape_()
    this.setOutput(true, null)
    this.setMutator(new Blockly.Mutator(['callback_def_create_with_item']))
    this.setTooltip()
  },
  mutationToDom: function () {
    const containerElement = Blockly.utils.xml.createElement('mutation')
    containerElement.setAttribute('items', this.itemCount_)
    return containerElement
  },

  domToMutation: function (xmlElements) {
    this.itemCount_ = parseInt(xmlElements.getAttribute('items'), 10)
    this.updateShape_()
  },

  decompose: function (workspace) {
    const containerOfBlock = workspace.newBlock(
      'callback_def_create_with_container'
    )
    containerOfBlock.initSvg()
    let connection = containerOfBlock.getInput('STACK').connection
    for (let i = 0; i < this.itemCount_; i++) {
      const itemBlockly = workspace.newBlock('callback_def_create_with_item')
      itemBlockly.initSvg()
      connection.connect(itemBlockly.previousConnection)
      connection = itemBlockly.nextConnection
    }
    return containerOfBlock
  },

  compose: function (containerBlock) {
    let itemBlock = containerBlock.getInputTargetBlock('STACK')
    // Count number of inputs.
    const connections = []
    while (itemBlock && !itemBlock.isInsertionMarker()) {
      connections.push(itemBlock.valueConnection_)
      itemBlock =
        itemBlock.nextConnection && itemBlock.nextConnection.targetBlock()
    }
    // Disconnect any children that don't belong.
    for (let i = 0; i < this.itemCount_; i++) {
      const connection = this.getInput('ADD' + i).connection.targetConnection
      if (connection && connections.indexOf(connection) === -1) {
        connection.disconnect()
      }
    }
    this.itemCount_ = connections.length
    this.updateShape_()
    // Reconnect any child blocks.
    for (let i = 0; i < this.itemCount_; i++) {
      Blockly.Mutator.reconnect(connections[i], this, 'ADD' + i)
    }
  },

  saveConnections: function (containerBlock) {
    let itemBlockly = containerBlock.getInputTargetBlock('STACK')
    let i = 0
    while (itemBlockly) {
      const input = this.getInput('ADD' + i)
      itemBlockly.valueConnection_ = input && input.connection.targetConnection
      i++
      itemBlockly =
        itemBlockly.nextConnection && itemBlockly.nextConnection.targetBlock()
    }
  },

  updateShape_: function () {
    if (this.itemCount_ && this.getInput('EMPTY')) {
      this.removeInput('EMPTY')
    } else if (!this.itemCount_ && !this.getInput('EMPTY')) {
      this.appendDummyInput('EMPTY').appendField('function')
    }
    // Add new inputs.
    // eslint-disable-next-line no-var
    let rep = 0
    for (let index = 0; index < this.itemCount_; index++) {
      if (!this.getInput('ADD' + index)) {
        const inputContent = this.appendValueInput('ADD' + index).setAlign(
          Blockly.ALIGN_RIGHT
        )
        if (index === 0) {
          inputContent.appendField('argument')
        }
      }
      rep = index
    }
    // Remove deleted inputs.
    rep++
    while (this.getInput('ADD' + rep)) {
      this.removeInput('ADD' + rep)
      rep++
    }
  }
}

Blockly.Blocks.callback_def_create_with_container = {
  /**
   * Mutator block for list container.
   * @this {Blockly.Block}
   */
  init: function () {
    this.setStyle('list_blocks')
    this.appendDummyInput().appendField('add argument')
    this.appendStatementInput('STACK')
    this.setTooltip('add argument')
    this.contextMenu = false
  }
}

Blockly.Blocks.callback_def_create_with_item = {
  /**
   * Mutator block for adding items.
   * @this {Blockly.Block}
   */
  init: function () {
    this.setStyle('list_blocks')
    this.appendDummyInput().appendField('argument')
    this.setPreviousStatement(true)
    this.setNextStatement(true)
    this.setTooltip('def')
    this.contextMenu = false
  }
}

Blockly.Python.callback_def_with_return = function (block) {
  // Create a list with any number of elements of any type.
  const name = block.getFieldValue('FUNCNAME')
  const elements = new Array(block.itemCount_)
  for (let i = 0; i < block.itemCount_; i++) {
    elements[i] =
      Blockly.Python.valueToCode(block, 'ADD' + i, Blockly.Python.ORDER_NONE) ||
      'None'
  }
  const code = name + '(' + elements.join(', ') + ')'
  return [code, Blockly.Python.ORDER_NONE]
}

Blockly.Blocks.callback_def_without_return = {
  init: function () {
    this.appendDummyInput().appendField(
      new Blockly.FieldLabelSerializable(''),
      'FUNCNAME'
    )
    this.setInputsInline(false)
    this.setPreviousStatement(true, null)
    this.setNextStatement(true, null)
    this.setHelpUrl()
    this.setColour(230)
    this.itemCount_ = 3
    this.updateShape_()
    this.setMutator(new Blockly.Mutator(['callback_def_create_with_item']))
    this.setTooltip()
  },

  mutationToDom: function () {
    const container = Blockly.utils.xml.createElement('mutation')
    container.setAttribute('items', this.itemCount_)
    return container
  },

  domToMutation: function (xmlElement) {
    this.itemCount_ = parseInt(xmlElement.getAttribute('items'), 10)
    this.updateShape_()
  },

  decompose: function (workspace) {
    const newBlock = workspace.newBlock('callback_def_create_with_container')
    newBlock.initSvg()
    let connection = newBlock.getInput('STACK').connection
    for (let i = 0; i < this.itemCount_; i++) {
      const itemBlock = workspace.newBlock('callback_def_create_with_item')
      itemBlock.initSvg()
      connection.connect(itemBlock.previousConnection)
      connection = itemBlock.nextConnection
    }
    return newBlock
  },

  compose: function (containerBlock) {
    let itemBlockly = containerBlock.getInputTargetBlock('STACK')
    // Count number of inputs.
    const connectionBlockly = []
    while (itemBlockly && !itemBlockly.isInsertionMarker()) {
      connectionBlockly.push(itemBlockly.valueConnection_)
      itemBlockly =
        itemBlockly.nextConnection && itemBlockly.nextConnection.targetBlock()
    }
    // Disconnect any children that don't belong.
    for (let i = 0; i < this.itemCount_; i++) {
      const connection = this.getInput('ADD' + i).connection.targetConnection
      if (connection && connectionBlockly.indexOf(connection) === -1) {
        connection.disconnect()
      }
    }
    this.itemCount_ = connectionBlockly.length
    this.updateShape_()
    // Reconnect any child blocks.
    for (let i = 0; i < this.itemCount_; i++) {
      Blockly.Mutator.reconnect(connectionBlockly[i], this, 'ADD' + i)
    }
  },

  saveConnections: function (containerBlock) {
    let itemBlockly = containerBlock.getInputTargetBlock('STACK')
    let i = 0
    while (itemBlockly) {
      const input = this.getInput('ADD' + i)
      itemBlockly.valueConnection_ = input && input.connection.targetConnection
      i++
      itemBlockly =
        itemBlockly.nextConnection && itemBlockly.nextConnection.targetBlock()
    }
  },

  updateShape_: function () {
    if (this.itemCount_ && this.getInput('EMPTY')) {
      this.removeInput('EMPTY')
    } else if (!this.itemCount_ && !this.getInput('EMPTY')) {
      this.appendDummyInput('EMPTY').appendField('function')
    }
    // Add new inputs.
    // eslint-disable-next-line no-var
    let rep = 0
    for (let j = 0; j < this.itemCount_; j++) {
      if (!this.getInput('ADD' + j)) {
        const input = this.appendValueInput('ADD' + j).setAlign(
          Blockly.ALIGN_RIGHT
        )
        if (j === 0) {
          input.appendField('argument')
        }
      }
      rep = j
    }
    // Remove deleted inputs.
    rep++
    while (this.getInput('ADD' + rep)) {
      this.removeInput('ADD' + rep)
      rep++
    }
  }
}

Blockly.Python.callback_def_without_return = function (block) {
  // Create a list with any number of elements of any type.
  const name = block.getFieldValue('FUNCNAME')
  const elements = new Array(block.itemCount_)
  for (let i = 0; i < block.itemCount_; i++) {
    elements[i] =
      Blockly.Python.valueToCode(block, 'ADD' + i, Blockly.Python.ORDER_NONE) ||
      'None'
  }
  const code = name + '(' + elements.join(', ') + ')\n'
  return code
}

export default Blockly
